/*
 * tobject.c
 *
 *  Created on: Jan 24, 2016
 *      Author: root
 */

#include <memory.h>
#include "../basic/operator.h"

static Cell* find_property_of_object(Scheme *sc, Cell *object, const char *propertyName) {
	return find_slot_by_symbol(sc, object, make_symbol(sc, propertyName));
}

//TODO
static Cell* find_method_by_name(Scheme *sc, TreeNode *node, Cell* methodName) {
	Cell *methods = cdar((Cell *)node->data);
	if (cell_contains_element(sc, methods, methodName)) {
		Cell* clazz = find_class_by_name(sc, make_symbol(sc, node->name));
		//return find_method_of_class(sc, clazz, methodName);
        return &g_nil;
	}
	return &g_nil;
}

//TODO
static Cell* find_method_of_object(Scheme *sc, Cell* objectName, Cell* methodName) {
	Cell* object = find_slot_in_env(sc, sc->env, objectName, FALSE);
	const char *className = object->_type;

	Cell* clazz = find_class_by_name(sc, make_symbol(sc, className));
	//Cell* method = find_method_of_class(sc, clazz, methodName);
    Cell* method = &g_nil;

	TreeNode * node = tree_find(sc->inheritance, className);
	while (method == &g_nil && strcmp(node->name, OBJECT_TYPE) != 0) {
		method = find_method_by_name(sc, node->parent, methodName);
	}
	if (method != &g_nil)
		return method;

	//method = find_method_by_name(sc, find_class_by_name(sc, make_symbol(sc, OBJECT_TYPE)), methodName);
	if (method != &g_nil)
		return method;

	//return new_exception("Cannot find method");
	return make_exception(sc, "METHOD_NOT_FOUND", "Cannot find method.", op_default_exception_handler, &g_nil);
}

/*
// (new-object circle Circle make-circle 10)
Cell* op_new_object(Scheme *sc) {
	//...
	return s_return_helper(sc, &g_true);
}
*/

Cell* make_object(Scheme *sc, Cell *className) {
	// get_op(); op_make_circle; my_g_hash_table_find("make-circle");

	Cell *object = &g_nil;

	/*
	if (is_primitive_class(className)) {
		// primitive op => object
	} else {
		Cell* properties = get_properties_of_class(sc, className);
		;
		// set slot values as &g_nil

		// properties is vector

		// int elements = 0;

		object = make_vector(sc, properties->_longValue);

		int length = properties->_longValue;
		int i = 0;
		Cell *elem;
		Cell *propertyName;
		Cell *propertyType;
		Cell *property;
		for (i = 0; i < length; i++) {
			elem = get_vector_item(properties, i);
			// elem is list, for elem
			// car(e) cdr(e)

			Cell *propertyValue = make_cell(sc, &g_nil, &g_nil);
			propertyValue->_type = propertyType->_string;

			//add_slot_by_symbol(Scheme *sc, Cell* vec, Cell* symbol, Cell* value)
			add_slot_by_symbol(sc, object, propertyName, propertyValue);
		}

	}
	*/

	return object;
}


// (send-message area circle)
Cell* op_send_message(Scheme *sc) {
	Cell* methodName = first(sc->args);
	Cell* objectName = second(sc->args);
	Cell* args = cddr(sc->args);
	Cell* method = find_method_of_object(sc, objectName, methodName);

	sc->code = method;
	sc->args = args;
	sc->op = cell_op_apply;
	return &g_true;
}

int type_of(Scheme *sc, Cell *obj, const char *type) {
	if (obj == &g_nil) {
		return FALSE;
	}
	if (strcmp(obj->_type, type) == 0) {
		return TRUE;
	}
	TreeNode *node = tree_find(sc->inheritance, obj->_type);
	TreeNode *p = node;
	while (p != NULL) {
		if (strcmp(p->name, type) == 0) {
			return TRUE;
		}
		p = p->parent;
	}
	return FALSE;
}

/*
void type_check(Scheme *sc, Cell *obj, const char *types) {
	if (!type_of(sc, obj, types)) {
		// throw exception
	}
}
*/

/* equivalence of atoms */
int eqv(Scheme *sc, Cell *a, Cell *b) {
	if (type_of(sc, a, BOOLEAN_TYPE) && type_of(sc, b, BOOLEAN_TYPE)) {
        return a == b;
    }
    if (type_of(sc, a, CHARACTER_TYPE) && type_of(sc, b, CHARACTER_TYPE)) {
        return ((a)->_longValue == (b)->_longValue);
    }
    if (type_of(sc, a, NUMBER_TYPE) && type_of(sc, b, NUMBER_TYPE)) {
        return eqn(a, b);
    }
    if (type_of(sc, a, SYMBOL_TYPE) && type_of(sc, b, SYMBOL_TYPE)) {
        return strcmp(symbol_name(a), symbol_name(b)) == 0;
    }
    if (type_of(sc, a, STRING_TYPE) && type_of(sc, b, STRING_TYPE)) {
        return strcmp(a->_string, b->_string) == 0;
    }
    if (type_of(sc, a, PROCEDURE_TYPE) && type_of(sc, b, PROCEDURE_TYPE)) {
        return a->_op == b->_op;
    }
    if (type_of(sc, a, EXCEPTION_TYPE) && type_of(sc, b, EXCEPTION_TYPE)) {
        return eqe(a, b);
    }
    return a == b;
}

int equal(Scheme *sc, Cell *a, Cell *b) {
    if (type_of(sc, a, PAIR_TYPE) && type_of(sc, b, PAIR_TYPE)) {
        return equal(sc, car(a), car(b)) && equal(sc, cdr(a), cdr(b));
    }
    return eqv(sc, a, b);
}

// (types-of? x types)
Cell* op_type_of(Scheme *sc) {
	Cell* x = first(sc->args);
	Cell* type = second(sc->args);
	TYPE_CHECK(sc, type, STRING_TYPE)
	int istrue = type_of(sc, x, type->_string);
	sc->returnValue = (istrue) ? &g_true : &g_false;
	return pop_callstack(sc);
}

// (null? x)
Cell* op_nullp(Scheme *sc) {
	int istrue = (first(sc->args) == &g_nil);
	sc->returnValue = (istrue) ? &g_true : &g_false;
	return pop_callstack(sc);
}

// (eq? x y)
Cell* op_eq(Scheme *sc) {
	int istrue = (first(sc->args) == second(sc->args));
	sc->returnValue = (istrue) ? &g_true : &g_false;
	return pop_callstack(sc);
}

// (eqv? x y)
Cell* op_eqv(Scheme *sc) {
	int istrue = eqv(sc, first(sc->args), second(sc->args));
	sc->returnValue = (istrue) ? &g_true : &g_false;
	return pop_callstack(sc);
}

/*
void init_object_operators(MyGHashTable *table) {
	;
}
*/
